<?php
namespace service;
use Exception;
use service\Video\VideoType;

/**
 * Class Video
 * @package service
 */
class Video{
    private $source;
    private $output;
    private $threads = 1;
    private $width= 320;
    private $height= 180;
    private $aspect = null;
    private $sprites = false;
    private $seconds = 2;
    private $libx264 = true;
    private $size = false;
    private $result = [];
    private $cmd = '';
    private $kbps = '200k';
    private $fps = '25';
    private $override = true;

    /**
     * Video constructor.
     * @param null $outPath
     * @throws Exception
     */
    public function __construct($outPath=null){
        $this->output =  $outPath??'.' . DIRECTORY_SEPARATOR;
        $this->checkRequirements();
    }

    protected $params = [
        'ffmpeg'=> 'ffmpeg',
        'source'=> '-i {source}',
        'copy'=> '-vcodec copy -an',
        'aspect'=> '-aspect {aspect}',
        'threads'=> '-threads {threads}',
        'sprites'=> '-qscale:v {seconds} image2 ./images/{source}_%08d.png',
        'libx264'=> '-an -vcodec libx264 -crf 23',
        'cover'=> '-r 1 -ss 00:00:01 -vframes 10 {size}',
        'size'=> '-s {size}',
        'kbps'=> '-b:v {kbps}',
        'override'=> '-y',
        'fps'=>'-r {fps}',
        'crop'=> '-ss {$start} -t {$end} -vcodec copy -acodec copy',
        'output'=> '{output}',
    ];

    /**
     * 设置大尺寸
     * @param int $width 宽
     * @param int $height 高
     * @param bool $auto 根据视频比例生成默认16:9
     * @return $this
     */
    public function setSize($width=0,$height=0,$auto=false){
        if($auto){
            if (!$width){
                $width = $this->width;
            }
            $aspect = $this->aspect??(16/9);
            $height = rand($width / $aspect,2);
            $this->size = "{$width}*{$height}";
        }else{
            if (!$width){
                $width = $this->width;
            }
            if (!$height){
                $height = $this->height;
            }
            $this->size = "{$width}*{$height}";
        }
        return $this;
    }

    /**
     * @param string $kbps
     * @return $this
     */
    public function setKbps($kbps=''){
        $this->kbps = $kbps;
        return $this;
    }

    /**
     * @param string $fps
     * @return $this
     */
    public function setFps($fps=''){
        $this->fps = $fps;
        return $this;
    }

    /**
     * @param bool $override
     * @return $this
     */
    public function setOverride($override=false){
        $this->override = $override;
        return $this;
    }

    /**
     * @param bool $libx264
     * @return $this
     */
    public function setLibx264($libx264=true){
        $this->libx264 = $libx264;
        return $this;
    }

    /**
     * @param bool $sprites
     * @return $this
     */
    public function setSprites($sprites=false){
        $this->sprites = $sprites;
        return $this;
    }

    /**
     * @param string $path
     * @return $this
     */
    public function setPath($path=''){
        $this->source = $path;
        return $this;
    }

    /**
     * @param int $seconds
     * @return $this
     */
    public function setSeconds($seconds=2){
        $this->seconds = $seconds;
        return $this;
    }

    /**
     * @param int $num
     * @return $this
     */
    public function setThreads($num=1){
        $this->threads = $num;
        return $this;
    }

    /**
     * @param int $num
     * @return $this
     */
    public function setWidth($num=250){
        $this->width = $num;
        return $this;
    }

    /**
     * @param int $num
     * @return $this
     */
    public function setHeight($num=140){
        $this->height = $num;
        return $this;
    }

    /**
     * @param string $aspect
     * @return $this
     */
    public function setAspect($aspect='4:3'){ //4:3 16:9 或 1.3333 1.7777
        $this->aspect = $aspect;
        return $this;
    }

    /**
     * @param string|null $path
     * @return $this
     */
    public function setOutPath(string $path=null){
        $this->output = $path??$this->output;
        return $this;
    }

    /**
     * @return array
     */
    public function getResult(){
        return $this->result;
    }

    /**
     * @return array
     */
    public function getCmd(){
        return [
            'status'=>1,
            'msg'=> '',
            'cmd'=>$this->cmd
        ];
    }

    /**
     * @return $this
     */
    public function createCmd() {
        $this->cmd = $this->map_cmd(func_num_args(),func_get_args());
        return $this;
    }

    /**
     * @param $path
     * @param string $out_path
     * @return $this
     * @throws Exception
     */
    public function convert($path=null,$out_path=null){
        $this->source = $path??$this->source;
        $this->output = $out_path??$this->output;
        if(!$this->source){
            $this->result = [
                'status'=> 0,
                'msg'=> 'is not a path.'
            ];
        }
        if(!is_file($this->source)){
            $this->result = [
                'status'=> 0,
                'msg'=> 'not a file.'
            ];
        }
        try {
            if(file_exists($this->output)){  // 清除已存在文件
                @unlink($this->output);
            }
            exec($this->cmd,$outputs,$return_var);
            if($return_var) {
                $this->result = [
                    'status'=> 0,
                    'msg'=> 'File "' . $path . '" does not convert to H264.'
                ];
                return $this;
            }else{
                $this->result = [
                    'status'=> 1,
                    'msg'=> 'success',
                    'originSrc' => $this->source,
                    'targetSrc'=>$this->output
                ];
                return $this;
            }
        } catch (Exception $e) {
            $this->result = [
                'status'=> 0,
                'msg'=> 'File "' . $path . '" does not convert to H264.'
            ];
            return $this;
        }
    }

//    public $cmd = [
//        'toAac'=>'ffmpeg -i {$source} -acodec aac -vn {$output}',
//        'toCopy'=>'ffmpeg -i {$source} -vcodec copy -an {$output}',
//        'toH264Hi'=>'ffmpeg -i {$source} -r 25 -s 1920*1080 -aspect 16:9 -b:v 2000K -bufsize 2000k -maxrate 2500k -vcodec h264 -strict -2 -acodec aac -ab 48k -ar 48000 -ac 2 {$output}',
//        'toH264Sprites'=>'ffmpeg -i {$source} -an -vcodec libx264 -crf 23 {$output} -qscale:v 2 ../cover/{$source}_%d.png',
//        'toH264Cover'=>'ffmpeg -i {$source} -an -vcodec libx264 -crf 23 -y -f image2 -t {$time} -s {$width}x{$height} {$output}'
//    ];
    /**
     * @param $nums
     * @param $numargs
     * @param string $fuc
     * @return array|string
     */
    private function map_cmd($nums,$numargs,$fuc=__FUNCTION__){
        if($nums==2){
            $this->source = $numargs[0];
            $this->output = $numargs[1];
        }elseif($nums==1){
            $this->source = $numargs[0];
            $pathinfo = pathinfo($this->source);
            $tmp = explode('.', $pathinfo['basename']);
            $this->output = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . $tmp[0] . '.mp4';
        }else{
            if(!$this->source){
                return [
                    'status'=> 0,
                    'msg'=> 'The convert File must be Enter.'
                ];
            }
            $pathinfo = pathinfo($this->source);
            $tmp = explode('.', $pathinfo['basename']);
            $this->output = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . $tmp[0] . '.mp4';
        }
        $_map = [];
        foreach ($this->params as $k => $v){
            try{
                if($this->$k){
//                    if(!$this->override && $k == 'output'){
//                        $_output = preg_replace("/\{$k\}/",$this->$k,$v);
//                        $pathinfo = pathinfo($_output);
//                        $_map[$k] = $pathinfo['dirname'] . DIRECTORY_SEPARATOR .$pathinfo['filename'].'_%t.'.$pathinfo['extension'];
//                    }else{
//                        $_map[$k] = preg_replace("/\{$k\}/",$this->$k,$v);
//                    }
                    $_map[$k] = preg_replace("/\{$k\}/",$this->$k,$v);
                }
            }catch (Exception $ex){}
        }
//        p($this);
//        p($_map);die;
        return 'ffmpeg '. implode(' ',$_map);
    }

    /*
    public function toAac($source='',$output=null)
    {
        if(!$source){
            throw new Exception('转换文件不存在');
        }
        if(!is_file($source)){
            throw new Exception('不是一个文件');
        }
        $pathinfo = pathinfo($source);
        $tmp = explode('.', $pathinfo['basename']);
        $output = $output?:$pathinfo['dirname'] . DIRECTORY_SEPARATOR . $tmp[0];
        $output .= '.aac';
        if(file_exists($output)){
            @unlink($output);
        }
        try {
            $command = str_replace('{$output}',$output,str_replace('{$source}',$source,$this->cmd[0]));
            exec($command,$outputs,$return_var);
            if($return_var) {
                throw new Exception('File "' . $source . '" does not convert to AAC.');
            }else{
                return true;
            }
        } catch (Exception $e) {
            throw new Exception('File "' . $source . '" does not convert.');
        }
    }

    public function toH264($source='',$output=null){

        if(!$source){
            throw new Exception('转换文件不存在');
        }
        if(!is_file($source)){
            throw new Exception('不是一个文件');
        }
        $pathinfo = pathinfo($source);
        $tmp = explode('.', $pathinfo['basename']);
        $output = $output?:$pathinfo['dirname'] . DIRECTORY_SEPARATOR . $tmp[0];
        $output .= '.mp4';
        if(file_exists($output)){
            @unlink($output);
        }
        try {
            $command = str_replace('{$output}',$output,str_replace('{$source}',$source,$this->cmd[4]));
            exec($command,$outputs,$return_var);
            if($return_var) {
                throw new Exception('File "' . $source . '" does not convert to H264.');
            }else{
                return true;
            }
        } catch (Exception $e) {
            throw new Exception('File "' . $source . '" does not convert.');
        }
    }

    public function toH264WithCover($source='',$output=null){
        if(!$source){
            throw new Exception('转换文件不存在');
        }
        if(!is_file($source)){
            throw new Exception('不是一个文件');
        }
        $pathinfo = pathinfo($source);
        $tmp = explode('.', $pathinfo['basename']);
        $output = $output?:$pathinfo['dirname'] . DIRECTORY_SEPARATOR . $tmp[0];
        $output .= '.mp4';
        if(file_exists($output)){
            @unlink($output);
        }
        try {
            $command = str_replace('{$output}',$output,str_replace('{$source}',$source,$this->cmd[5]));
            exec($command,$outputs,$return_var);
            if($return_var) {
                throw new Exception('File "' . $source . '" does not convert to H264.');
            }else{
                return true;
            }
        } catch (Exception $e) {
            throw new Exception('File "' . $source . '" does not convert.');
        }
    }
     */

    /**
     * @throws Exception
     */
    private function checkRequirements(){
        // Check OS
        if(PHP_OS != 'Linux') {
            throw new Exception('Operating system is not supported. The program only runs on Linux server.');
        }

        // Check if liberoffice has been installed
        exec('ffmpeg -version > NULL && echo yes || echo no',$output,$return_value);
        if($return_value) {
            throw new Exception('The progress ffmpeg is killed.');
        }
        if($output[0] == 'no') {
            throw new Exception('ffmpeg has not been installed.');
        }
    }

    public function kbps(VideoType $videoType){

    }
}